#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <config.h>
#include <draw.h>
#include <encoding_manager.h>
#include <fonts_manager.h>
#include <display_manager.h>
#include <string.h>

typedef struct PageDesc{
	int iPage;
	unsigned char *puclcdFirstPosAtFile;
	unsigned char *puclcdNextFirstPosAtFile;
	struct PageDesc *ptPrePage;
	struct PageDesc *ptNextPage;
}T_PageDesc, *PT_PageDesc;

static int g_iFdTextFile;
static int g_iFontSize;

static struct stat g_tFileBuf;
static unsigned char *g_pucTextFileMem;
static unsigned char *g_pucTextFileMemEnd;
static unsigned char *g_pucLcdFirstPosAtFile;
static unsigned char *g_puclcdNextPosAtFile;

static PT_EncodingOpr g_ptEncodingOprForFile;
static PT_DispOpr g_ptSelectedDisplay;

static PT_PageDesc g_ptCurPage = NULL;
static PT_PageDesc g_ptPageHead = NULL;

int OpenTextFile(char *pcFileName)
{
	g_iFdTextFile = open(pcFileName, O_RDONLY);
	if(g_iFdTextFile < 0)
	{
		DEG_PRINTF("Can't open %s \n", pcFileName);
		return -1;
	}

	if(fstat(g_iFdTextFile, &g_tFileBuf))
	{
		DEG_PRINTF("Can't get file stat\n");
		return -1;
	}

	g_pucTextFileMem = (unsigned char *)mmap(NULL, g_tFileBuf.st_size, PROT_READ, MAP_SHARED, g_iFdTextFile, 0);
	if(g_pucTextFileMem == (unsigned char *)-1)
	{
		DEG_PRINTF("Can't mmap %s \n", pcFileName);
		return -1;
	}

	g_pucTextFileMemEnd = g_pucTextFileMem + g_tFileBuf.st_size;

	g_ptEncodingOprForFile = SelectEncodingForFile(g_pucTextFileMem);
	if(g_ptEncodingOprForFile)
	{
		g_pucLcdFirstPosAtFile = g_pucTextFileMem + g_ptEncodingOprForFile->iHead;
		return 0;
	}
	else
	{
		return -1;
	}
}

int SetFontDetail(char *pucHzkFile, char *pucFreetypeFile, unsigned int dwFontSize)
{
	int iError = 0;
	int iRet = -1;
	PT_FontOpr ptFontOpr;	
	PT_FontOpr ptTmp;

	g_iFontSize = dwFontSize;

	ptFontOpr = g_ptEncodingOprForFile->ptFontOprSupportedHead;
	while(ptFontOpr)
	{
		if(strcmp(ptFontOpr->name, "ascii") == 0)
		{
			iError = ptFontOpr->FontInit(NULL, dwFontSize);
		}
		else if(strcmp(ptFontOpr->name, "gbk") == 0)
		{
			iError = ptFontOpr->FontInit(pucHzkFile, dwFontSize);
		}
		else
		{
			iError = ptFontOpr->FontInit(pucFreetypeFile, dwFontSize);
		}
		
		DEG_PRINTF("%s, %d\n", ptFontOpr->name, iError);
		ptTmp = ptFontOpr->ptNext;
		
		if(iError == 0)
		{
			iRet = 0;
		}
		else
		{
			DelFontOprFrmEncoding(g_ptEncodingOprForFile, ptFontOpr);
		}
		ptFontOpr = ptTmp;
	}
	return iRet;
}

int SelectAndInitDisplay(char *pcDisplayName)
{
	int iError;
	g_ptSelectedDisplay = GetDispOpr(pcDisplayName);
	DEG_PRINTF("SelectAndInitDisplay %s\n", g_ptSelectedDisplay->name);
	if(!g_ptSelectedDisplay)
	{
		return -1;
	}

	iError = g_ptSelectedDisplay->DeviceInit();
	return iError;
}

int GetDisplayResolution(int *piXres, int *piYres)
{
	*piXres = g_ptSelectedDisplay->iXres;
	*piYres = g_ptSelectedDisplay->iYres;
	return 0;
}

int RecordPage(PT_PageDesc ptPageDesc)
{
	PT_PageDesc ptPage;

	if(g_ptPageHead == NULL)
	{
		g_ptPageHead = ptPageDesc;
		ptPageDesc->ptNextPage = NULL;
	}
	else
	{
		ptPage = g_ptPageHead;
		while(ptPage->ptNextPage)
		{
			ptPage = ptPage->ptNextPage;
		}
		ptPage->ptNextPage = ptPageDesc;
		ptPageDesc->ptPrePage = ptPage;
	}
	return 0;
}

int IncLcdY(int iY)
{
	if(iY + g_iFontSize > g_ptSelectedDisplay->iYres)
		return 0;
	else
		return (iY + g_iFontSize);
}

int RelocateFontPos(PT_Bitmap ptBitmap)
{
	int iLcdY;
	int iDeltaX;
	int iDeltaY;

	if(ptBitmap->iYMax > g_ptSelectedDisplay->iYres)
	{
		return -1;
	}

	if(ptBitmap->iXMax > g_ptSelectedDisplay->iXres)
	{
		iLcdY = IncLcdY(ptBitmap->iCurOriginY);
		if(iLcdY == 0)
		{
			return -1;
		}
		else
		{
			iDeltaX = 0 - ptBitmap->iCurOriginX;
			iDeltaY = iLcdY - ptBitmap->iCurOriginY;
			
			ptBitmap->iCurOriginX  += iDeltaX;
			ptBitmap->iCurOriginY  += iDeltaY;

			ptBitmap->iNextOriginX += iDeltaX;
			ptBitmap->iNextOriginY += iDeltaY;

			ptBitmap->iXLeft += iDeltaX;
			ptBitmap->iXMax  += iDeltaX;

			ptBitmap->iYTop  += iDeltaY;
			ptBitmap->iYMax  += iDeltaY;
			return 0;
		}
	}
	return 0;
}

int ShowOneFont(PT_Bitmap ptFontBitmap)
{
	int x, y;
	int bit;
	int i = 0;
	unsigned char ucByte = 0;
	if(ptFontBitmap->iBpp == 1)
	{
		for(y = ptFontBitmap->iYTop; y < ptFontBitmap->iYMax; y++)
		{
			i = (y - ptFontBitmap->iYTop)*ptFontBitmap->iPitch;
			for(x = ptFontBitmap->iXLeft, bit = 7; x < ptFontBitmap->iXMax; x++)
			{
				if(bit == 7)
				{
					ucByte = ptFontBitmap->pucBuffer[i++];
				}

				if(ucByte & (1<<bit))
				{
					g_ptSelectedDisplay->ShowPixel(x, y, COLOR_FOREGROUND);
				}
				bit--;
				if(bit == -1)
					bit = 7;
			}
		}
	}	
	else if (ptFontBitmap->iBpp == 8)
	{
		for (y = ptFontBitmap->iYTop; y < ptFontBitmap->iYMax; y++)
			for (x = ptFontBitmap->iXLeft; x < ptFontBitmap->iXMax; x++)
			{
				//g_ptDispOpr->ShowPixel(x, y, ptFontBitMap->pucBuffer[i++]);
				if (ptFontBitmap->pucBuffer[i++])
					g_ptSelectedDisplay->ShowPixel(x, y, COLOR_FOREGROUND);
			}
	}
	else
	{
		DEG_PRINTF("ShowOneFont error, can't support %d bpp\n", ptFontBitmap->iBpp);
		return -1;
	}
	return 0;
}

int ShowOnePage(unsigned char *pucTextFileCurPos)
{
	int iError;
	int iLen;
	unsigned char *pucBufStart;
	unsigned int dwCode;

	PT_FontOpr ptFontOpr;
	T_Bitmap tFontBitmap;
	
	int bHasGetCode = 0;
	int bHasNotClearScreen = 1;

	tFontBitmap.iCurOriginX = 0;
	tFontBitmap.iCurOriginY = g_iFontSize;
	
	pucBufStart = pucTextFileCurPos;
	while(1)
	{
		iLen = g_ptEncodingOprForFile->GetCodeFrmBuffer(pucBufStart, g_pucTextFileMemEnd, &dwCode);
		//DEG_PRINTF("%s %d\n", g_ptEncodingOprForFile->name, dwCode);
		if(iLen == 0)
		{
			if(!bHasGetCode)
			{
				return -1;
			}
			else
			{
				return 0;
			}
		}

		bHasGetCode = 1;

		pucBufStart += iLen;

		if(dwCode == '\n')
		{
			g_puclcdNextPosAtFile = pucBufStart;

			tFontBitmap.iCurOriginX = 0;
			tFontBitmap.iCurOriginY = IncLcdY(tFontBitmap.iCurOriginY);
			if(0 == tFontBitmap.iCurOriginY)
			{
				return 0;
			}
			else
			{
				continue;
			}
		}
		else if(dwCode == '\r')
		{
			continue;
		}
		else if(dwCode == '\t')
		{
			dwCode = ' ';
		}

		ptFontOpr = g_ptEncodingOprForFile->ptFontOprSupportedHead;
		while(ptFontOpr)
		{	
			iError = ptFontOpr->GetFontBitmap(dwCode, &tFontBitmap);
			if(iError == 0)
			{
				if(RelocateFontPos(&tFontBitmap))
				{
					return 0;
				}

				if(bHasNotClearScreen)
				{
					g_ptSelectedDisplay->ClearScreen(COLOR_BACKGROUND);
					bHasNotClearScreen = 0;
				}

				if(ShowOneFont(&tFontBitmap))
				{
					return -1;
				}

				tFontBitmap.iCurOriginX = tFontBitmap.iNextOriginX;
				tFontBitmap.iCurOriginY = tFontBitmap.iNextOriginY;

				g_puclcdNextPosAtFile = pucBufStart;
				break;
			}
			ptFontOpr = ptFontOpr->ptNext;
		}
	}
	return 0;
}

int ShowNextPage(void)
{
	int iError;
	unsigned char *pucTextFileCurPos;
	PT_PageDesc ptPageTmp;

	if(g_ptCurPage)
	{
		pucTextFileCurPos = g_ptCurPage->puclcdNextFirstPosAtFile;
	}
	else
	{
		pucTextFileCurPos = g_pucLcdFirstPosAtFile;
	}
	iError = ShowOnePage(pucTextFileCurPos);
	if(iError == 0)
	{
		if(g_ptCurPage && g_ptCurPage->ptNextPage)
		{
			g_ptCurPage = g_ptCurPage->ptNextPage;
			return 0;
		}

		ptPageTmp = malloc(sizeof(T_PageDesc));
		if(ptPageTmp)
		{
			ptPageTmp->puclcdFirstPosAtFile = pucTextFileCurPos;
			ptPageTmp->puclcdNextFirstPosAtFile = g_puclcdNextPosAtFile;
			ptPageTmp->ptNextPage = NULL;
			ptPageTmp->ptPrePage  = NULL;
			g_ptCurPage = ptPageTmp;
			RecordPage(ptPageTmp);
			return 0;
		}
		else
		{
			return -1;
		}
	}
	return iError;
}

int ShowPrePage(void)
{
	int iError;
	
	if(!g_ptCurPage || !g_ptCurPage->ptPrePage)
	{
		return -1;
	}

	iError = ShowOnePage(g_ptCurPage->ptPrePage->puclcdFirstPosAtFile);
	if(iError == 0)
	{
		g_ptCurPage = g_ptCurPage->ptPrePage;
	}
	return iError;
}

